package cc.permission.domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 权限
 *
 * @author wangyan
 * @date 2021/6/11 10:28
 */
public class Permission1 {
    // 为了移位计算，<< 相当于 *64；>> 相当于 /64
    private final static long ADDRESS_BITS_PER_WORD = 6L;
    private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD; // 64
    // 当前创建了几个 long 空间
    private transient int longUsed = 0;

    private long[] resources;

    public Permission1(){
        // 64
        initResources(BITS_PER_WORD);
    }

    public void addResource(List<Long> resourceIdList){
        for (Long resourceId : resourceIdList){
            addResource(resourceId);
        }
    }

    public void addResource(long resourceId){
        // 比如 resourceIndex = 65，则定位到第1个 long 空间中
        int resourceIndex = resourcesIndex(resourceId);
        expandTo(resourceIndex);
        // {0,1,2,65} → [00000...0111,00000...0010] 即 [7,2]
        resources[resourceIndex] |= (1L << resourceId);
    }

    // 返回[7,2]，二进制的long类型表现实行
    public long[] toLongArray() {
        return Arrays.copyOf(resources, longUsed);
    }

    /**
     * 初始化：比如我需要70个 bit 位，则需要两个 long 空间，即 long[2]
     *
     * @param nBits 需要几个 bit 位
     * @author wangyan
     * @date 2021/7/30 10:57
     */
    private void initResources(int nBits) {
        // 64 -1 = 63；63 / 64 = 0；0 + 1 = 1；new long[1]
        resources = new long[resourcesIndex(nBits-1) + 1];
    }

    /**
     * 右移 6 位，相当于除以 2的6次方（64）
     * 确定资源的 index 在第几个 long 空间
     *
     * @param resourceIndex 资源位置
     * @return int
     * @author wangyan
     * @date 2021/7/30 9:31
     */
    private static int resourcesIndex(long resourceIndex) {
        return (int)(resourceIndex >> ADDRESS_BITS_PER_WORD);
    }

    /**
     * 扩展 long 空间
     *
     * @param resourceIndex 资源在第几个 long 空间
     * @author wangyan
     * @date 2021/7/30 11:01
     */
    private void expandTo(int resourceIndex) {
        // 假设{65}号资源，现在定位 long[1] 了，那么现在需要的long空间长度为2
        int sizeRequired = resourceIndex + 1;
        // 如果需要的长度 > 当前的长度；则扩容
        if (longUsed < sizeRequired) {
            ensureCapacity(sizeRequired);
            longUsed = sizeRequired;
        }
    }

    private void ensureCapacity(int sizeRequired) {
        // 需要2个long空间，但是当前 long[] 长度只有1
        if (resources.length < sizeRequired) {
            // 分配更大的两倍大小或所需的大小
            int request = Math.max(2 * resources.length, sizeRequired);
            resources = Arrays.copyOf(resources, request);
            // sizeIsSticky = false;
        }
    }

    // 最后一个 long 空间如果是 0，则移除，[5,0] → [5]
    public static Permission1 valueOf(long[] resources){
        int n;
        for (n = resources.length; n > 0 && resources[n - 1] == 0; n--);
        return new Permission1(Arrays.copyOf(resources, n));
    }

    private Permission1(long[] resources) {
        this.resources = resources;
        this.longUsed = resources.length;
    }

    // [7,2] -> {0,1,2,65}
    public static List<Long> getPermissionPoints(List<Long> resourceCollect) {
        List<Long> idList = new ArrayList<>();
        for (int i = 0; i < resourceCollect.size(); i++) {
            long longPermission = resourceCollect.get(i);
            for (int j = 0;j < 64;j++){
                if(convertLongToBinaryString(longPermission,j)){
                    idList.add((long)(i * 64 + j));
                }
            }
        }
        return idList;
    }
    private static boolean convertLongToBinaryString(long num, long digits) {
        return (num & (1L << digits)) != 0;
    }

    // [1,2] or [1,3,4]
    public void or(Permission1 permission1) {
        if (this == permission1)
            return;
        // 两个 long 空间，最小的，即公共的 2
        int wordsInCommon = Math.min(longUsed, permission1.longUsed);

        // 如果当前的小，则扩容；[1,2,0]
        if (longUsed < permission1.longUsed) {
            ensureCapacity(permission1.longUsed);
            longUsed = permission1.longUsed;
        }

        // 计算公共的 or；[1,2] or [1,3]
        for (int i = 0; i < wordsInCommon; i++)
            resources[i] |= permission1.resources[i];

        // 把 4 直接copy到[1,2]
        if (wordsInCommon < permission1.longUsed)
            System.arraycopy(permission1.resources, wordsInCommon,
                    resources, wordsInCommon,
                    longUsed - wordsInCommon);
    }

    public static void main(String[] args){
        /*List<Long> resourceIdList = new ArrayList<>();
        resourceIdList.add(0L);
        resourceIdList.add(1L);
        resourceIdList.add(2L);
        resourceIdList.add(65L);
        Permission1 permission1 = new Permission1();
        permission1.addResource(resourceIdList);
        System.out.println(Arrays.toString(permission1.toLongArray()));*/

        Permission1 permission11 = Permission1.valueOf(new long[]{0, 2});
        System.out.println(Arrays.toString(permission11.toLongArray()));
        System.out.println(permission11.toString());
    }
}